package com.emeraldparser.util;

import java.util.Collection;
import java.util.Set;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;

public class SetGraph<V, E> extends AbstractGraph<V, E> {

	private final Collection<V> vertices;
	private final Collection<Edge<V, E>> edges;
	private final Multimap<V, Edge<V, E>> edgesByVertex = HashMultimap.create();

	private SetGraph() {
		final Set<V> vertices = Sets.newHashSet();
		
		this.vertices = new StandardBulkModificationForwardingCollection<V>() {

			@Override
			protected Collection<V> delegate() {
				return vertices;
			}
			
			@Override
			public boolean remove(Object object) {
				boolean modified = super.remove(object);
				
				if (modified) {
					Collection<Edge<V, E>> edgesToRemove = edgesByVertex.removeAll(object);
					edges.removeAll(edgesToRemove);
				}
				
				return modified;
			}

			@Override
			public void clear() {
				super.clear();
				
				// Without any vertices, there can be no edges
				edges.clear();
				edgesByVertex.clear();
			}
			
		};
		
		Set<Edge<V, E>> edges = Sets.newHashSet();
		
		edges = new SurjectiveMappingSet<Edge<V, E>, V>(
				edges,
				Graphs.<V, E>edgeSourceFunction(),
				edgesByVertex
			);
		
		edges = new SurjectiveMappingSet<Edge<V, E>, V>(
				edges,
				Graphs.<V, E>edgeDestinationFunction(),
				edgesByVertex
			);
		
		this.edges = edges;
	}

	public static <V, E> SetGraph<V, E> create() {
		return new SetGraph<V, E>();
	}

	@Override
	public Collection<V> vertices() {
		return vertices;
	}

	@Override
	public Collection<Edge<V, E>> edges() {
		return edges;
	}

	/**
	 * Overriding {@link AbstractGraph#edges(Object)} to take advantage of 
	 * the map we have been maintaining to speed up access to a vertex's edges.
	 * 
	 */
	@Override
	public Collection<Edge<V, E>> edges(V vertex) {
		// TODO make sure changes to this collection affect the original collection
		return edgesByVertex.get(vertex);
	}

	@Override
	public String toString() {
		return "Graph[vertices=" + vertices() + ", edges=" + edges() + "]";
	}

}
